package serverMgr

import (
	"context"
	"encoding/json"
	"fmt"
	"path"
	"src/gcommon"
	"src/zeus/common"
	"src/zeus/etcd"
	"src/zeus/iserver"
	"src/zeus/logger"
	"src/zeus/threads"
	"strings"
	"time"
)

/*
使用ETCD进行服务发现
*/
type DiscoveryMgr struct {
	server *iserver.ServerInfo //本服务器信息
	thgo   *threads.ThreadGo   //协程管理器
	//上报时间
	refreshTime time.Duration

	//默认负载均衡器
	defmap IServerMap
	//监听对应的负载均衡器
	watchmap map[string]IServerMap
}

func NewDiscoveryMgr(server *iserver.ServerInfo, thgo *threads.ThreadGo) *DiscoveryMgr {
	result := new(DiscoveryMgr)
	result.refreshTime = 10 * time.Second
	result.server = server
	// result.dsync = new(sync.Once)
	// result.serverMapRW = new(sync.RWMutex)
	result.defmap = NewServerMapByRand()
	result.watchmap = make(map[string]IServerMap)
	if thgo == nil {
		result.thgo = threads.NewThreadGo()
	} else {
		result.thgo = threads.NewThreadGoByGo(thgo)
	}
	dismgr = result
	//为etcd设置出问题时的函数
	//这个过程 应该在ETCD开始监听之前就要设置好。不然会有安全问题
	if !gcommon.GetCfgInst().Config.Debug {
		etcd.SetEtcdTimeoutFunc(result.ClearServerInfoAll)(etcd.GetEtcd())
	}
	return result
}

// 开始注册
func (mgr *DiscoveryMgr) Register() (err error) {

	etcd.GetEtcd().SetReFunc(mgr.register)
	err = mgr.register()
	mgr.registerLoad()
	//load属性的更新,把服务注册方法，写入etcd，让系统可以在租约过期的时候，重新上去
	return nil
}

func (mgr *DiscoveryMgr) register() (err error) {
	server := mgr.server
	key := GetPath(server.Group, server.Type, server.ServerID)
	val, _ := json.Marshal(server)
	// if strings.Contains(server.OuterAddress, "127.0.0.1") {
	//这里的代码需要正常的时候去掉
	// if flags.GetFlagByBool(common.PrivateService) {
	// 	//如果是本地服，需要把有效时间去掉
	// 	if err := etcd.GetEtcd().PutForever(key, string(val)); err != nil {
	// 		return err
	// 	}
	// } else {
	if err = etcd.GetEtcd().Put(key, string(val)); err != nil {
		logger.Warn("Register Etcd error:", key, string(val), err)
		//logger.Error("Register Etcd error:", key, string(val), err)
	}
	// }
	return
}

// load属性的更新，系统默认会5秒上报一次load
func (mgr *DiscoveryMgr) registerLoad() {
	ctrl := iserver.GetSrvInst()
	server := mgr.server
	key := GetPathLoad(server.Group, server.Type, server.ServerID)
	// keygateway := GetGateWayPathLoad(server.Group, server.ServerID)
	li := make([]int, 10)
	tmp := ctrl.GetLoad()
	for i := range li {
		li[i] = tmp
	}
	i := 0
	top := tmp
	mgr.thgo.Go(func(ctx context.Context) {
		tk := time.NewTicker(mgr.refreshTime)
		defer tk.Stop()
		for {
			select {
			case <-ctx.Done():
				return
			case <-tk.C:
				li[i] = ctrl.GetLoad()
				top = 0
				for k := range li {
					top += li[k]
				}
				top = top / 10
				val := fmt.Sprint(top)
				etcd.GetEtcd().Put(key, val)
				i = (i + 1) % 10
			}
		}
	})
}

// 主动上报Load
func (mgr *DiscoveryMgr) UpLoad() {
	ctrl := iserver.GetSrvInst()
	server := mgr.server
	key := GetPathLoad(server.Group, server.Type, server.ServerID)
	val := fmt.Sprint(ctrl.GetLoad())
	etcd.GetEtcd().Put(key, val)
}

// 监听Key，不要重复
func (mgr *DiscoveryMgr) Watch(groupid uint32, stype iserver.ServerType2, smap IServerMap) {
	//监听一般在服务器启动的时候，开起来就好了，后面不会再有操作了
	var key string
	if groupid == iserver.GroupGlobalID {
		key = GetWatchByType(stype) // key=/src/type/ 监听某个类型的所有服务器
	} else {
		key = GetWatchPath(groupid, stype) // key=/src/type//groupid 监听的某个类型的某个组的服务器
	}
	if _, ok := mgr.watchmap[key]; ok {
		//因为已有自定义监听了，所以不再进行监听了。
		if smap != nil {
			logger.Error("Duplicate watch key:", key)
		}
		return
	}

	mgr.watchWithKey(smap, key)
}
func (mgr *DiscoveryMgr) WatchByKey(prefix string, smap IServerMap) {
	//监听一般在服务器启动的时候，开起来就好了，后面不会再有操作了
	if _, ok := mgr.watchmap[prefix]; ok {
		//因为已有自定义监听了，所以不再进行监听了。
		logger.Error("Duplicate watch key:", prefix)
		return
	}
	mgr.watchWithKey(smap, prefix)
}

// Login监听所有Gateway的Key，不要重复
// same as: mgr.Watch(0, iserver.ServerTypeGateway, smap)
func (mgr *DiscoveryMgr) WatchGateway(smap IServerMap) {

	//监听一般在服务器启动的时候，开起来就好了，后面不会再有操作了
	key := GetWatchGateWayPath() // key=/src/type/ 监听网关类型的所有服务器
	if _, ok := mgr.watchmap[key]; ok {
		//因为已有自定义监听了，所以不再进行监听了。
		logger.Error("Duplicate watch key:", key)
		return
	}
	mgr.watchWithKey(smap, key)
}

func (mgr *DiscoveryMgr) watchWithKey(smap IServerMap, key string) {
	if smap == nil {
		smap = mgr.defmap
	}
	mgr.watchmap[key] = smap

	etcd.GetEtcd().WatchPrefix(key, smap.WatchCallDelay)
	smap.LoadWatchAll(key)
	etcd.GetEtcd().SetReFunc(func() error {
		smap.LoadWatchAll(key)
		return nil
	})
}

// Login 获取Gateway服务器信息,并且根据负载均衡算法，选择一个服务器
func (mgr *DiscoveryMgr) GetGatewayServerInfoByGroup(groupid uint32) (result *iserver.ServerInfo, err error) {
	stype := uint8(iserver.ServerTypeGateway)
	key := GetWatchGateWayPath()
	if smap, ok := mgr.watchmap[key]; ok {
		return smap.GetBalancing(groupid, stype)
	} else {
		return mgr.defmap.GetBalancing(groupid, stype)
	}
}

// 根据负载逻辑，获取服务器信息,并且根据负载均衡算法，选择一个服务器
func (mgr *DiscoveryMgr) GetServerByType(groupid uint32, stype uint8) (result *iserver.ServerInfo, err error) {
	key := GetWatchPath(groupid, stype)
	if smap, ok := mgr.watchmap[key]; ok {
		return smap.GetBalancing(groupid, stype)
	} else {
		return mgr.defmap.GetBalancing(groupid, stype)
	}
}

// 根据负载逻辑，获取服务器
func (mgr *DiscoveryMgr) GetServerListByType(groupid uint32, stype uint8) ([]iserver.ServerInfo, error) {
	return mgr.GetServerList(groupid, stype)
}

// 根据负载逻辑，获取服务器
func (mgr *DiscoveryMgr) GetServerList(groupid uint32, stype uint8) ([]iserver.ServerInfo, error) {
	key := GetWatchPath(groupid, stype)
	if smap, ok := mgr.watchmap[key]; ok {
		return smap.GetServerList(groupid, stype)
	} else {
		return mgr.defmap.GetServerList(groupid, stype)
	}
}

// GetServerById 根据服务器类型和ID取服务器
func (mgr *DiscoveryMgr) GetServerById(groupid uint32, stype uint8, serverId uint64) (*iserver.ServerInfo, error) {
	key := GetWatchPath(groupid, stype)
	if smap, ok := mgr.watchmap[key]; ok {
		return smap.GetServerByID(groupid, stype, serverId)
	} else {
		return mgr.defmap.GetServerByID(groupid, stype, serverId)
	}
}

// 获取Gway的列表
func (mgr *DiscoveryMgr) GetGwayList() ([]iserver.ServerInfo, error) {
	key := GetWatchGateWayPath()
	if smap, ok := mgr.watchmap[key]; ok {
		return smap.GetServerList(0, 0)
	} else {
		return mgr.defmap.GetServerList(0, 0)
	}
}

// 清空服务器信息，因为可能是脏数据
func (mgr *DiscoveryMgr) ClearServerInfoAll() {
	logger.Info("Etcd leaseID timeout. DiscoveryMgr.ClearServerInfoAll.")
	mgr.defmap.DeleteAll()
	for _, smap := range mgr.watchmap {
		smap.DeleteAll()
	}
}

func (mgr *DiscoveryMgr) GetServerMap(key string) IServerMap {
	if smap, ok := mgr.watchmap[key]; ok {
		return smap
	} else {
		return mgr.defmap
	}
}

// 设置服务器状态 state=-1 灰度下线; state=0 正常模式
func (mgr *DiscoveryMgr) SetServerState(state int) {
	// 设置当前服务器信息状态为-1，不允许后续访问
	// 如果有调用的地方,需注意外部加锁,server属性本身是外部传递的
	logger.Info("&&& 当前服务器 设置灰度离线状态为:", mgr.server.Status, " -> ", state)
	mgr.server.Status = state
	mgr.register()
}

// 获取指定小区服务器类型 的服务器ID列表
func (_ *DiscoveryMgr) GetEtcdSvrIDsByGroupID(gid uint32, stype iserver.ServerType2) (result []int32) {
	key := GetWatchPath(gid, stype)
	req, err := etcd.GetEtcd().GetPathAll(key)
	if err != nil {
		logger.Warnf("GetEtcdSvrIDsByGroupID %s", err)
		return
	}
	for _, val := range req.Kvs {
		paths := strings.Split(string(val.Key), "/")
		if len(paths) >= 4 {
			if sid, err := common.NewString(paths[3]).ToInt64(); err == nil {
				if common.ArrayIndexof(result, int32(sid)) == -1 {
					result = append(result, int32(sid))
				}
			}
		}
	}
	return result
}

// 获取指定小区服务器类型 的服务器ID列表
func GetEtcdSvrInfosByGroupID(GroupID uint32, stype iserver.ServerType2) (result []*iserver.ServerInfo) {
	etcdKey := GetWatchPath(GroupID, stype)
	etcdResponse, err := etcd.GetEtcd().GetPathAll(etcdKey)
	if err != nil {
		logger.Warn("[handler] etcd.GetPathAll fail ", etcdKey, err.Error())
		return nil
	}
	for _, kv := range etcdResponse.Kvs {
		_, sidstr := path.Split(string(kv.Key)) //like "src/$srvType/$groupId/$srvId/info"
		switch sidstr {
		case PATH_INFO:
			sinfo := new(iserver.ServerInfo)
			if err := json.Unmarshal(kv.Value, sinfo); err != nil {
				//可能需要输出信息
				continue
			}
			result = append(result, sinfo)
		}
	}
	return
}
